10W - Vault를 활용한 CICD 보안
개요
이번 시간에는 하시코프의 볼트를 이용해 시크릿을 관리하는 방법을 알아본다.
EKS와 명확하게 관련되는 것은 아니나, 운영에서 활용하기 좋은 토픽들에 대한 주제로서 스터디가 진행됐다.
사전지식
볼트란?
하시코프에서 만든 시크릿 중앙 집중 관리 시스템.
신원 기반으로 시크릿과 암호화 관리를 해준다.
오픈소스로서 있으나, 엔터프라이즈 상품도 존재한다.
시크릿 관리 필요성
여기에서 말하는 시크릿은 기밀하게 저장하거나 다뤄야 하는 다양한 데이터들을 총칭한다.
이런 데이터들은 다양한 영역에서 사용되어 저장되는 위치도 제각각인 경우가 많다.
또한 데이터를 평문 상태로 디스크에 저장하는 케이스도 왕왕 있다.
결국 엄격하지 않은 저장 방식과 넓은 공격 표면은 보안 상 큰 위협을 야기하고 운영 조직의 관리를 어렵게 만든다.
그래서 시크릿을 통합적으로, 중앙 집중화하여 관리할 수 있는 솔루션으로 vault가 등장한 것이다.
볼트의 기능
볼트가 가진 대표적인 기능은 다음과 같다.
- 시크릿을 보관하는 스토리지
- 임의의 키와 값을 볼트에 저장할 수 있다.
- 볼트는 이 데이터들을 암호화해 저장해준다.
- 데이터는 디스크나 외부 스토리지에 저장될 수 있다.
- 동적 시크릿
- AWS와 같은 클라우드나 데이터베이스에 요구하는 형식의 시크릿을 동적으로 생성하여 제공하는 것도 가능하다.
- aws로 치면 볼트가 aws로부터 access key를 받아와서 클라에게 제공하고, 클라는 이를 aws에서 활용할 신원으로 사용할 수 있다.
- 볼트가 토큰을 받고자 하는 해당 시스템과 통신해 필요한 시크릿을 받아 클라가 시스템에서 활용할 수 있도록 해준다.
- AWS와 같은 클라우드나 데이터베이스에 요구하는 형식의 시크릿을 동적으로 생성하여 제공하는 것도 가능하다.
- 데이터 암호화
- 스토리지로서 데이터를 암호화해 저장하는 것 말고도, 암호화된 데이터를 복호화하거나, 암호화를 하는 기능도 제공한다.
- 대여와 갱신
- 볼트의 모든 시크릿은 일정 기간 동안 유효하도록 세팅된다.
- 그래서 영구적으로 확보되어 보안 위협을 초래하는 시크릿 없이 상대적으로 안전하게 토큰만 받아서 활용할 수 있다.
- 이에 대한 간편한 갱신 방법도 제공한다.
- 회수, 폐기
- 토큰 유효한 기간 이전에 필요에 따라 토큰을 만료시키거나 회수하는 로직이 볼트에 내장돼있다.
- 이를 통해 특정 유저로부터 읽힌 비밀, 혹은 특정 유형의 비밀 계층 구조 전체를 폐기하는 등 다양한 동작을 수행할 수 있다.
사용 흐름
간단하게, 볼트는 위의 흐름을 통해 기능을 제공한다.
- 인증 - 어떤 클라이언트가 볼트에 접근했는지 신원 정보를 얻어내고, 이를 서버 내부에서 사용할 클라 토큰으로 만든다.
- 검증 - LDAP, AppRole, OIDC 등의 서드 파티 소스로부터 클라이언트를 명확하게 검증한다.
- 인가 - 볼트의 보안 정책에 따라 클라가 얻을 수 있는 것들을 체크한다.
- 접근 - 클라에게 정책에 맞게 적절한 토큰을 제공한다.
이 흐름도만으로는 설명이 조금 부족하다고 느껴서 조금 더 보충하자면, 크게 두 흐름으로 분리하여 보는 게 좋다.
(참고로 동적 시크릿 접근, AppRole 인증 방식 등에서는 조금 더 플로우가 추가되니 간단하게만 도식화한 거라 봐주면 되겠다.)
클라이언트는 볼트에게 먼저 토큰을 발급 받아야 한다.
이때 클라는 어떤 방식으로든 신원을 인증할 방식을 골라서 요청을 해야 하고, 볼트는 다양한 방식으로 검증한다.
검증이 완료되면 볼트는 클라이언트에게 토큰을 제공한다.
이제 클라이언트는 이 토큰을 가지고 볼트에 요청을 날려 원하는 시크릿을 얻어오면 된다.
(보통은 헤더에 X-Vault-Token
에 해당 토큰을 넣어서 날린다.)
이때 볼트는 이 토큰을 클라의 신원(identity)으로 삼아 여러 정책을 기반으로 어떤 데이터를 받아오기에 적절한 권한이 있는지 인가를 결정하는 과정을 거친다.
인가가 된다면 볼트는 해당 시크릿을 반환해주는 구조이다.
이 시크릿이 무엇이 될 지, 구체적으로 어떤 절차를 거치는지는 어떤 시크릿 엔진을 쓰냐에 따라 다른데, 이건 [[#시크릿 엔진]]에서 보자.
큰 구조는 복잡하지 않은 게, 그냥 토큰을 받고, 이 토큰으로 시크릿을 얻는다는 것이 주 골자이다.
볼트 개념 정리
볼트를 사용하기 위해 알아야 하는 개념들이 몇 가지 존재한다.
위에서 간략하게 봤던 기능과 동작들인데, 이 단락에서는 그 개념들을 간략하게 다뤄본다.
밀봉(seal), 개봉(unseal)
간단하게 seal은 봉인하는 것을 말한다.
이 단어는 봉투에 도장 꽉 찍어서 봉인을 한다는 어감이 들어 있어서, 봉투를 봉인할 때 쓰는 표현인 밀봉으로 번역했다.
반댓말로 개봉이란 표현을 unseal의 번역어로 사용하면 되니 더더욱 적절하다고 생각한다.
볼트에는 밀봉(seal) 개념이 존재하는데, 볼트 서버가 처음 초기화될 때 보게 된다.
밀봉 상태의 볼트는 어떠한 설정과 조작을 수행할 수 없다.
그래서 일련의 조작을 가하기 위해서는 개봉(unseal) 작업을 거쳐야 한다.
왜 이런 번거로운 절차가 들어가있는가?
볼트 서버는 기본적으로 관리하는 데이터를 암호화하여 저장하고 있기 때문이다.
이때 볼트가 사용하는 암호키를 아무나 알 수 없도록 볼트 내부적으로 또다시 암호화를 하고 있는데, 이 키를 루트 키라고 한다.
다시 말해 볼트 역시 사용자에게 암호화된 데이터를 복호화하여 전달하기 위해서는 루트 키를 가지고 있어야만 한다.
그래서 이 키를 만들어내는 과정이 바로 개봉 작업인 것이다.
루트 키는 Shamir Secret Sharing 방식을 이용해 얻을 수 있다.
문서를 보면 알겠지만 이 알고리즘은 임계치(threshold)만큼 개봉용키를 받아야만 개봉이 되고, 개봉용 키는 여러 개를 둘 수 있다.
이를 기반으로 볼트를 개봉하는 행위에 책임자를 분산하고, 단일 공격 표면으로부터의 위협을 줄일 수 있다.
맨 처음 볼트를 가동하면 볼트를 조작하기 위한 인증 수단으로서 사용되는 것이 또 이 루트키이니, 어딘가에 잘 저장해두던가 하자.
한번 개봉한 볼트는 reseal 조작을 통해 다시 밀봉 상태로 만들 수 있다.
근데 서버가 재시작되거나, 스토리지 접근이 불가능해질 때도 볼트는 알아서 밀봉 상태가 된다.
모종의 사고로 밀봉이 돼버렸을 때를 위해 recovery key를 세팅하는 것이 가능하다.
참고로 사미르 방식 말고 Key Management System, Hardware Security Module을 통해 자동 봉인 해제가 되도록 할 수도 있다.
그리고 고가용성 세팅을 해서 볼트를 여러 개 띄운다고 해도, 밀봉은 각각에 적용되므로 각각 풀어줘야 한다.
경로(path)
본격적으로 개념들을 이해하기에 앞서, 볼트에 요청을 보내는 방법에 대해서 알 필요가 있다.
볼트 자체도 결국 하나의 웹서버이다.
그래서 볼트에 조작을 하거나 시크릿을 가져오거나 하는 모든 과정은 http를 통해 이뤄진다.
이때 인증을 수행하는가, 시크릿을 가져오는가, 볼트 설정을 하는가에 따라 크게 세 개의 api가 나뉜다.
그리고 앞으로 보게 될 인증과 시크릿에 관해서, 볼트에서는 path라는 개념을 제공한다.
api로 따지면 그냥 엔드포인트를 나누는 건데, auth api에서 다양한 인증을 path 단위로 관리한다고 보면 된다.
자세한 예시는 [[#인증]]에서 참고하고, 아무튼 시크릿 쪽도 이런 식으로 구성됐다고 이해하면 되겠다.
그림을 가져온 김에 이야기하자면 볼트 서버의 모든 영역은 일단 barrier라는 암호화 계층에 포함된다.
실제 데이터는 스토리지 백엔드에 저장되는데, 배리어 계층이 데이터의 암호화를 보장한다.
인증
볼트에 접근하는 클라를 어떻게 신원 파악하는가?
볼트는 다양한 인증 메서드를 지원한다.
- 루트 토큰 - [[#밀봉(seal), 개봉(unseal)]]에서 말한 루트키를 말하며, 이건 비활성화될 수 없는 인증 방식이다.
- userpass(일반 로그인 방식) - 이 경우 유저 정보가 볼트에 저장될 것이다.
- OIDC
- LDAP
- Github 토큰
- AppRole
- Kubernetes - 클러스터에서 api 서버에 인증을 의탁
- 등..
여러 수단을 통해 클라를 인증하고, 이를 통해 클라이언트 토큰을 발급한다.
이 토큰이 볼트에서 시크릿을 가져오기 위한 기반이 된다.
여러 인증 메서드가 있고, 이것들을 여러 개 중복해서 세팅하는 것이 가능하다.
다양한 인증 메서를 다양한 설정을 두어 사용할 수 있도록 볼트에서는 인증의 단위를 경로(path)로 마련할 수 있게 해준다.
vault auth enable -path=auth1 userpass
vault auth enable -path=auth2 userpass
vault auth enable -path=auth2 kubernetes
이런 식으로, 여러 메서드를 이용할 수 있으면서 이 메서드들을 여러개, 중복으로 세팅할 수 있다.
인증 관련하여 볼트에서는 auth
api를 제공하고 있다.
AppRole
위 메서드 대부분은 인증을 볼트 외부에 위임하는 것들인데, 이건 userpass와 더불어 볼트 내부에서 인증을 처리하는 방식이다.
유저가 직접 활용하기 보다는 자동화가 가능한 어플리케이션에 활용하라고 만들어진 거라 이름도 AppRole이다.
구제적으로, 볼트 단에서 먼저 클라가 사용할 롤을 정의한다.
vault write auth/approle/login \
role_id=db02de05-fa39-4855-059b-67221c5c2f63 \
secret_id=6a174c20-f6de-a53c-74d2-6018fcceff64
그러면 클라는 위와 같이 이 롤의 신원을 얻기 위해 로그인이란 작업을 수행하면 된다.
이때 사용되는 값이 두 개가 있는데, role_id는 이 롤을 나타내는 고유 식별자이다.
secret_id는 이 롤을 사용하기 위해 발급 받는 비번이다.
이렇게 로그인 과정을 거치면 비로소 토큰이 나오며, 이 토큰을 이용해 시크릿에 접근하면 된다.
여기에서는 토큰의 유형을 간단하게만 정리하겠다.
- Service
hvs.<랜덤>
형식을 가진 토큰- 볼트의 가장 기본적인 토큰이다.
- Batch
hvb.<랜덤>
형식을 가진 토큰- 이건 경량화된 토큰으로, 볼트에서 수행할 수 있는 정보와 기능을 암호화한 일종의 데이터 blob 파일이다.
- 왜 경량화됐다는 것이냐, 토큰 크기 자체는 클 수 있겠지만 이건 스토리지에 저장되지 않기에 메모리 단에서 모든 기능을 수행할 수 있다.
- 대신 서비스 토큰만큼 다양한 볼트의 기능을 적용할 수는 없고 간단하게만 활용될 수 있다.
실질적으로 활용을 많이 할 토큰은 서비스, 배치 토큰인데, 이 둘을 문서에서 잘 비교하고 있어 그대로 긁어왔다.
배치 토큰은 보다시피 서비스 토큰의 상당한 기능들을 사용하지 못하며, 가볍게 사용할 때만 유용할 것이다.
볼트에서 토큰에는 계층 구조가 성립할 수 있다.
어떤 토큰이 다른 토큰을 만드는데 사용되던가 하는 식으로 설정이 될 수 있다는 것이다.
부모 토큰이 회수되면 자식 토큰도 한꺼번에 회수된다.
그래서 운영 조직에서는 한 팀 당 토큰을 두고, 여기에 자식 토큰을 여러 개 둬서 사용하는 식으로 설정을 할 수 있다.
회사 사정에 따라 한 팀을 쳐낼 때 매우 유용하겠읍니다
위에서 말했듯 토큰은 일단 ttl을 가진다.
설정에 따라 주기적 토큰을 만들어서 알아서 자동 갱신이 되는 영구 토큰을 만드는 것도 가능하다.
디비 같은 상태유지가 중요한 작업을 수행할 때 필요한 토큰에 유용할 것이다.
주기적 갱신이 되는 것일 뿐이니, 엄밀히 영구는 아니며 당연히 회수도 가능하다.
임대(lease), 갱신(renew), 회수(revoke)
볼트는 토큰을 임대해준다.
다시 말해 볼트는 토큰을 줄 때 토큰의 만료 기한, 갱신 여부 등의 속성을 부여한다는 말이다.
그래서 토큰을 받을 때, lease_id라는 것도 같이 볼 수 있을 것이다.
기간이 만료된다면 볼트는 해당 토큰을 회수하며, 클라가 받은 토큰은 무용지물이 된다.
이 방식이야 너무나도 당연한 보안적 이점을 가지고 있기에 자세한 설명은 생략한다.
회수 작업은 만료를 통해서만 이뤄지는 것이 아니라 특정 조건을 충족하거나 관리자의 설정에 의해서도 이뤄질 수 있다.
탈취당한 토큰을 무효화한다던가 하는 설정을 할 수 있다는 것도 볼트의 큰 장점 중 하나이다.
참고로 볼트가 자체적으로 저장한 시크릿에는 임대 개념이 없다!
정책
그럼 토큰에 부여한다는 정책이란 것은 어떤 식으로 세팅되는가?
정책은 흔히 생각하는 RBAC이다.
어떤 대상에 대해 어떤 조작을 할 수 있는지를 정하는 정책을 설정한다.
그리고 이 정책은 토큰에 부여한다.
위에서 보았듯 토큰은 다양한 인증 메서드를 설정해둔 상태에서 클라의 요청에 대해 반환되는 것이다.
그래서 실질적으로 이 정책은 그 인증 방식에 대해 부여하는 것이라 할 수 있다.
다음의 예시를 생각해보자.
- 설정 단계
- 관리팀이 볼트에 userpass 인증 메서드로 auth1이라는 경로(path)를 마련했다.
- 관리팀은 auth1에 대해 1시간 짜리 aws 액세스 키를 받을 수 있도록 정책을 부여했다.
- 사용 단계
- 이제 개발팀은 auth1 경로에 요청을 보내 id와 비번을 쳐서 토큰을 받는다.
- 이 토큰으로 볼트에 다시금 요청을 날리는데, auth1으로 생성된 토큰이니 관리팀이 정한 정책에 따라 1시간 짜리 access key를 받는다.
정책은 HCL 방식으로 작성한다(hcl에 대해서는 Terraform#HCL 문법 참고).[1]
path "secret/foo" {
capabilities = ["read"]
}
위 예시는 시크릿 중 foo 라는 경로에 대해 읽기 권한을 부여하는 정책이다.
더 자세한 설정과 방식은 공식 문서를 참고하자.
볼트의 시크릿 엔진
볼트는 다양한 시크릿들을 유형 별로 엔진으로 분리하여 관리한다.
그리고 인증 쪽과 마찬가지로 여러 경로로 이를 설정할 수 있도록 한다.
대표적인 것들 몇 가지만 보겠다.
KV
key value 시크릿 엔진은 일반적인 키값 기반의 데이터를 저장하는 엔진이다.
볼트가 직접적으로 데이터를 저장하는 유형은 바로 이것에 해당한다.
1과 2 두 개의 버전이 있는데, 2 버전부터는 한 키에 대해 값의 버전을 기록할 수 있다.
2버전의 경우 버전 정보에 따라 metadata까지 저장을 해야 하기에 보다시피 엔드포인트 상의 변화가 조금 생겼다.
대체로 다른 시크릿 엔진은 볼트 외부의 시크릿 데이터를 얻기 위한 설정들인 경우가 많다.
당장 아래 aws만 보더라도 실제 aws에 저장된 어떤 데이터를 받기 위해 시크릿 엔진을 세팅하는 게 아니라, aws에서 활용할 수 있는 신원 수단을 받는 방식이다.
그러나 kv 엔진의 경우 정말 볼트에 데이터를 저장하는 것이라 여기에 대해 시크릿을 가져오는 행위는 정말 해당 데이터를 가져오는 행위가 된다.
즉, 이게 바로 [[#기능]]에서 언급한 시크릿 스토리지로서의 볼트의 기능이다.
반면 다른 시크릿 엔진들의 경우에는 동적 시크릿 기능이라고 보면 되겠다.
k8s
쿠버네티스에 대해 제공하는 시크릿 엔진으로, 서비스 어카운트 토큰, 롤 바인딩, 롤 등의 정보를 저장할 수 있다.
인증 수단으로 쿠버네티스를 쓰는 것과 별개로 쿠버네티스 내의 데이터를 저장하기 위한 시크릿 엔진인 거니까 헷갈리지 말자.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: k8s-full-secrets-abilities
rules:
- apiGroups: [""]
resources: ["serviceaccounts", "serviceaccounts/token"]
verbs: ["create", "update", "delete"]
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["rolebindings", "clusterrolebindings"]
verbs: ["create", "update", "delete"]
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["roles", "clusterroles"]
verbs: ["bind", "escalate", "create", "update", "delete"]
위의 말한 것들을 처리하기 위해, 클러스터에서 돌아가고 있는 볼트라면 사용하는 서비스 어카운트에 위의 권한을 부여해야 한다.
vault write kubernetes/roles/my-role \
allowed_kubernetes_namespaces="*" \
service_account_name="test-service-account-with-generated-token" \
token_default_ttl="10m"
그다음에는 어떤 네임스페이스의 어떤 서비스 어카운트의 정보를 저장할 건지 이렇게 세팅해주면 된다.
현재 쿠버네티스는 서비스어카운트에 생명 기한을 가진 토큰을 발급하는 api가 있는데, 볼트는 이걸 활용해 토큰을 제공해줄 것이다.
아예 요런 식으로 제공해준다.
이 서비스어카운트 토큰을 그대로 이용해주면 되는 것이다.
클러스터 내 워크로드가 이걸 사용하게 하려는 것이라면 이를 도와주는 에이전트를 사용하면 유용하다.
아예 볼트로 모든 인증 인가를 관리하는 조직이라면, 사람이 쓸 토큰을 아예 이렇게 발부하는 것도 괜찮을 수도..?
더 이상 유저가 아니라 서비스 어카운트가 되어버린ww
vault write kubernetes/roles/auto-managed-sa-role \
allowed_kubernetes_namespaces="test" \
kubernetes_role_name="test-role-list-pods"
어떤 롤이 바인딩된 서비스 어카운트를 자동으로 만들고 싶다면 이런 식으로 해주면 된다.
볼트 에이전트
볼트에서 시크릿 엔진 별로 토큰을 부여하고, 이 토큰에 정책을 매핑하는 등의 기본적인 개념들을 알아봤다.
근데 이런 과정을 사람이 일일히 매번 진행하는 것은 당연히 번거로운 일이기에, 볼트에서는 이를 클라이언트 측에서 자동화할 수 있는 방식을 제공한다.
바로 에이전트와 프록시로, 이들은 자동 인증과 토큰 발급 역할을 수행할 수 있다.
특정 에이전트 툴을 반드시 다운 받아 사용해야 하는 건 당연히 아니고, 언어 라이브러리를 통해 이를 직접 개발하여 에이전트로 두는 것도 가능하다.
복잡해보이지만, 그냥 위에서 언급한 동작 흐름을 어플리케이션 단에서 진행하지 않고 에이전트가 수행해서 받아온 시크릿을 어플리케이션은 사용만 하면 되는 구조이다.
이런 구조를 두면 좋은 게, 자동 인증을 하는 것도 가능하고 시크릿 데이터를 간단하게 캐싱하는 것도 가능하다.
실습 진행
이번 실습에서는 볼트를 세팅하고, 간단하게 쿠버네티스 클러스터에서 워크로드에 에이전트를 자동 주입하는 실습을 해본다.
이후에는 CICD 툴인 젠킨스와 아르고 CD에 연동하는 작업을 해본다.
실습 환경 세팅
클러스터, cicd 환경 구축
먼저 클러스터는 KIND를 이용한다.
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: myk8s
networking:
apiServerAddress: "127.0.0.1"
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30000
hostPort: 30000
- containerPort: 30001
hostPort: 30001
- containerPort: 30002
hostPort: 30002
- containerPort: 30003
hostPort: 30003
- containerPort: 30004
hostPort: 30004
- containerPort: 30005
hostPort: 30005
- containerPort: 30006
hostPort: 30006
- role: worker
- role: worker
3개의 노드를 가진 클러스터로 구성했다.
kind create cluster --config kind.yaml
이후에 젠킨스는 따로 구축했다.
services:
jenkins:
container_name: jenkins
image: jenkins/jenkins
restart: unless-stopped
networks:
- cicd-network
ports:
- "8080:8080"
- "50000:50000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- jenkins_home:/var/jenkins_home
volumes:
jenkins_home:
networks:
cicd-network:
driver: bridge
젠킨스는 도커 컴포즈로 배포하여 클러스터와 별개의 환경으로 관리한다.
docker compose up -d
위 설정에 따라 사이트는 8080 포트로 열린다.
dex:
enabled: false
server:
service:
type: NodePort
nodePortHttps: 30002
extraArgs:
- --insecure
아르고 cd도 최대한 간략하게 세팅했다.
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd --version 7.8.13 -f argocd-values.yaml --namespace argocd --create-namespace
아르고 cd는 30002번 포트로 접근될 것이다.
그럼 이제 젠킨스와 아르고 cd 초기 세팅을 해보자.
docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
비번은 이렇게 찾아서 넣어주면 된다.
초기 플러그인은 그냥 추천된 것들 해줘도 된다.
(젠킨스 쓰기 싫었는데. 오랜만에 보니까 ui가 더 이뻐진 것 같기도)
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d ;echo
admin 계정에 비번을 치고 들어오면 기본 준비는 끝.
볼트 세팅
global:
enabled: true
tlsDisable: true # Disable TLS for demo purposes
server:
image:
repository: "hashicorp/vault"
tag: "1.19.0"
standalone:
enabled: true
replicas: 1 # 단일 노드 실행
config: |
ui = true
disable_mlock = true
cluster_name = "vault-local"
listener "tcp" {
address = "[::]:8200"
cluster_address = "[::]:8201"
tls_disable = 1
}
storage "raft" { # Raft 구성 권장
path = "/vault/data"
node_id = "vault-dev-node-1"
}
service:
enabled: true
type: NodePort
port: 8200
targetPort: 8200
nodePort: 30000 # Kind에서 열어둔 포트 중 하나 사용
injector:
enabled: true
ui:
enabled: true
serviceType: "NodePort"
볼트를 헬름으로 설치한다.
여러 설정이 조금 들어갔는데, 스왑 메모리에 데이터가 저장되지 않도록 하고 RAFT로 클러스터를 구성하는 세팅이다.
(레플리카가 하나라 딱히 의미는 없다.)
helm repo add hashicorp https://helm.releases.hashicorp.com
helm upgrade vault hashicorp/vault -n vault -f vault-values.yaml --install --create-namespace
처음 세팅을 진행하면 볼트가 레디 상태가 되지 않는 것을 볼 수 있다.
로그를 보면 밀봉 설정에 대한 이슈라는 것을 알 수 있다.
kubectl exec -ti vault-0 -- vault status
볼트 명령어로 보면 봉인 상태가 됐고, 이에 따라 초기화가 진행되지 않았다는 것을 확인할 수 있다.
개봉
VAULT_POD="vault-0"
alias VAULT_CMD="kubectl exec -ti $VAULT_POD -- vault"
먼저 간단한 세팅을 몇 개 해주었다.
VAULT_CMD operator init -key-shares=3 -key-threshold=2
임계치를 2로 설정하면 최소 2개의 키가 들어와야만 루트 키를 얻어낼 수 있다.
굳이 3개의 키를 만들어달라고 할 필요는 없지만 어떤 식으로 출력되는지 보고 싶어 이렇게 해봤다.
보다시피 3개의 키가 나온 것이 확인된다.
출력 상에서 바로 루트 키를 보여주고 있는데, 이건 쉐어링 키를 조합해서 나오는 마스터 키를 의미하는 게 아니다!
쉐어링 키를 이용해 복호화되는 키는 vault 내부에 들어있어 사용자는 이를 확인할 수 없다.
현재 출력된 루트 키는 해금이 된 이후에 vault에 처음 접근할 때 사용하는 키로, 볼트 내부에는 저장되지 않기 때문에 처음 출력된 이후로 다시 확인할 수 없다.
그러니 이 키도 잘 저장해두자.
VAULT_CMD operator unseal
바로 키를 전부 넣어 봉인을 푸는 것도 가능하나, 하나씩 넣어보았다.
키를 하나 넣었을 때, 해금 프로세스가 하나 증가하는 것을 볼 수 있다.
해금이 완료되자 비로소 레디 상태가 됐다.
위에서 본 루트 키를 이용해 로그인한다.
볼트 cli 설치
web ui로도 보겠지만, cli로도 접근하여 설정을 조금 더 편리하게 해보자.
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install vault
공식 문서만 봤을 때, vault 전용 cli를 다운 받는 방식이 분리돼있지는 않은 것 같다.
애초에 vault가 쿠버네티스 전용 툴도 아니니 당연히 cli가 클라-서버 구조로 설계되지 않은 것이 이상한 일은 아니라고 생각한다.
그래도 크기가 조금 크긴 하다..
export VAULT_ADDR='http://localhost:30000'
아무튼 cli로 현재 로컬에 볼트를 설정할 것은 아니기에, vault의 주소를 환경변수로 등록하여 사용한다.[2]
이제 클러스터에 위치한 볼트를 로컬 cli로 조작할 수 있게 됐다.
이렇게 나오면 성공!
vault login
을 통해 cli에서도 조작을 할 수 있도록 루트키를 입력해준다.
KV 시크릿 엔진 활성화 후 정적 시크릿 생성
볼트 자체를 스토리지로 사용하는 KV 시크릿 엔진을 먼저 간단하게 사용해보자.
vault secrets enable -path=secret kv-v2
vault kv put secret/sampleapp/config \
username="demo" \
password="p@ssw0rd"
vault kv get secret/sampleapp/config
성공적으로 데이터가 들어간 것이 확인된다.
이때 시크릿 경로를 보면 put을 할 때 secret/sampleapp/config
로 넣었는데도 중간에 data라는 경로가 추가된 것을 볼 수 있다.
볼트의 kv 시크릿 엔진 v2는 시크릿의 버전 관리를 해주기에 들어온 시크릿을 데이터와 메타데이터를 구분해서 관리한다.
이에 따라 실제 데이터는 data라는 중간 경로를 포함해서 경로가 노출되는 것이다.
페이지로 가서 봐도 추가된 것을 잘 확인할 수 있다.
해당 시크릿을 꺼내기 위해 어떻게 api 접근을 하면 되는지, cli로는 어떻게 인자를 넣어야 하는지도 잘 나와서 사용성은 좋은 것 같다.
클러스터 워크로드에 볼트 에이전트 주입- 사이드카 인젝터 활용
그럼 이제 이 볼트에서 시크릿을 클러스터 내 워크로드가 꺼내오도록 하는 실습을 진행한다.
헬름으로 볼트를 설치할 때, injector 필드를 설정했기에 어노테이션만 간단하게 다는 것으로 에이전트를 주입하는 것이 가능하다.
흔히 그렇듯, 볼트에서도 사람이 아닌 계정이나 어플리케이션에 대해 인증이나 권한을 설정하는 방식이 AppRole로 따로 마련돼있다.
그런데 쿠버네티스 환경에서는 쿠버네티스의 인증 인가 방식을 활용하면서 에이전트를 사이드카로 자동 주입을 할 수 있도록 세팅을 지원하기 때문에 이쪽을 위주로 실습을 진행해본다.
apiVersion: v1
kind: Secret
metadata:
name: vault
annotations:
kubernetes.io/service-account.name: vault
type: kubernetes.io/service-account-token
일단 볼트에서 클러스터 api에 접근하기 위한 서비스 어카운트 토큰을 만들어둔다.
볼트의 서비스 어카운트는 system:auth-delegator
를 받는데, 이건 토큰 리뷰와 확인을 요청할 수 있는 권한을 가지고 있다.
이를 기반으로 볼트는 시크릿을 접근하려는 요청의 jwt 토큰의 신원을 파악할 수 있게 된다.
vault auth enable kubernetes
vault write auth/kubernetes/config \
token_reviewer_jwt="$(kubectl get secret vault -o go-template='{{ .data.token }}' | base64 --decode)" \
kubernetes_host="https://kubernetes.default.svc.cluster.local:443" \
kubernetes_ca_cert="$(kubectl config view --raw -o jsonpath='{.clusters[?(@.name=="'"$(kubectl config current-context)"'")].cluster.certificate-authority-data}' | base64 -d)" \
disable_local_ca_jwt=true
먼저 쿠버네티스 인증 방식을 활성화한다.
클러스터 루트 인증서와 호스트, 그리고 토큰을 요청하게 될 서비스어카운트의 토큰을 넣어서 설정하는 것을 확인할 수 있다.
이 문서를 참고해 아예 서비스 어카운트 시크릿도 만들고 이를 초반에 주입하는 식으로 해결했다.[3]
vault read auth/kubernetes/config
값이 제대로 세팅됐는지 확인한다.
vault policy write sampleapp-policy - <<EOF
path "secret/data/sampleapp/*" {
capabilities = ["read"]
}
EOF
vault write auth/kubernetes/role/sampleapp-role \
bound_service_account_names="vault-ui-sa" \
bound_service_account_namespaces="vault" \
policies="sampleapp-policy" \
ttl="24h"
위 참고 단락을 따라하지 않았다면 정책을 만들어줘야 한다.
그리고 해당 정책을 받게 될 쿠버네티스 롤(이 롤은 쿠버 네이티브 리소스 롤이 아니라 하나의 주체라 보면 되겠다)을 지정한다.
vault write auth/kubernetes/login role="sampleapp-role" jwt="$(kubectl create token vault-ui-sa)"
이렇게 해서 로그인 테스트를 해봤을 때 token이 나오면 성공이다.
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault-ui-sa
namespace: vault
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: vault-injected-ui
namespace: vault
spec:
replicas: 1
selector:
matchLabels:
app: vault-injected-ui
template:
metadata:
labels:
app: vault-injected-ui
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "sampleapp-role"
vault.hashicorp.com/agent-inject-token: "true"
vault.hashicorp.com/agent-inject-secret-config.json: "secret/data/sampleapp/config"
vault.hashicorp.com/agent-inject-template-config.json: |
{{- with secret "secret/data/sampleapp/config" -}}
{
"username": "{{ .Data.data.username }}",
"password": "{{ .Data.data.password }}"
}
{{- end }}
vault.hashicorp.com/agent-inject-output-path: "/vault/secrets"
spec:
serviceAccountName: vault-ui-sa
containers:
- name: app
image: python:3.10
ports:
- containerPort: 5000
command: ["sh", "-c"]
args:
- |
pip install flask && cat <<PYEOF > /app.py
import json, time
from flask import Flask, render_template_string
app = Flask(__name__)
while True:
try:
with open("/vault/secrets/config.json") as f:
secret = json.load(f)
break
except:
time.sleep(1)
@app.route("/")
def index():
return render_template_string("<h2>🔐 Vault Injected UI</h2><p>👤 사용자: {{username}}</p><p>🔑 비밀번호: {{password}}</p>", **secret)
app.run(host="0.0.0.0", port=5000)
PYEOF
python /app.py
---
apiVersion: v1
kind: Service
metadata:
name: vault-injected-ui
namespace: vault
spec:
type: NodePort
ports:
- port: 5000
targetPort: 5000
nodePort: 30001
selector:
app: vault-injected-ui
오토 인젝팅 방식을 사용할 때는 인젝팅될 워크로드에 어노테이션을 다는 방식으로 세팅한다.
보면 모든 세팅을 일일히 어노테이션으로 넣고 있는데, configmap을 만들고 이것을 어노테이션으로 넣어 설정을 전달하는 방법도 존재하긴 한다.
실행하면 이렇게 알아서 컨테이너가 주입된다.
초기화 컨테이너와 일반 컨테이너가 주입되는 것을 확인할 수 있는데, 초기화 컨테이너가 실질 로그인을 담당한다.
이렇게 데이터가 출력되면 성공이다!
웹 ui에서 시크릿을 조금 수정하고 파드에 실제 반영이 되는지 체크해본다.
현재 실습 코드 로직 상으로는 변경된 시크릿 데이터를 다시 읽어오진 않기에 그냥 파드에 exec으로 확인해본다.
k logs vault-injected-ui-77fb865789-z9bxd -c vault-agent
사이드카의 로그를 보면 동적으로 시크릿을 다시 렌더링하여 파일에 반영했다는 것을 알 수 있다.
실습용 컨테이너에 직접 데이터를 뜯어봐도 제대로 데이터는 반영된것을 확인할 수 있다.
젠킨스 연동 - AppRole 인증
이제 본격적으로 cicd 툴과 연동하는 실습을 진행한다.
먼저, 젠킨스가 사용할 신원을 제공하기 위해 AppRole 인증 방식을 활용해본다.
vault auth enable approle
vault auth list
approle 인증 방식이 활성화된 것을 볼 수 있다.
vault policy write sampleapp-policy - <<EOF
path "secret/data/sampleapp/*" {
capabilities = ["read"]
}
EOF
vault write auth/approle/role/sampleapp-role \
token_policies="sampleapp-policy" \
secret_id_ttl="1h" \
token_ttl="1h" \
token_max_ttl="4h"
다음으로 kv 시크릿에 읽기 권한을 허용하는 정책을 만들고, 이 정책을 approle에 붙여준다.
approle 인증 경로에 어떤 정책을 매핑하기 위해 write를 하는 행위 자체가 하나의 role을 만드는 행위이다.
사용될 토큰의 기본 수명과, 시크릿id의 유효기간도 지정했다.
이렇게 되면 성공이다.
ROLE_ID=$(vault read -field=role_id auth/approle/role/sampleapp-role/role-id)
SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/sampleapp-role/secret-id)
mkdir -p approle-creds
echo "$ROLE_ID" > approle-creds/role_id.txt
echo "$SECRET_ID" > approle-creds/secret_id.txt
롤 id와 시크릿 id는 이렇게 받아온다.
앱롤 방식은 일정 수명을 가진 신원 토큰을 먼저 받는 개념이기에 시크릿 id는 쓰기 작업으로 가져온다.
이제 본격적으로 젠킨스 세팅을 진행한다.
docker network connect kind jenkins
컴포즈 파일에서 미리 external 설정을 하면 좋은데, 젠킨스를 띄우면서 이걸 간과했다.
아쉬운대로 젠킨스도 카인드 네트워크를 활용할 수 있게 해둔다.
젠킨스에 볼트 플러그인을 먼저 설치해준다.
이제 플러그인 세팅을 본격적으로 해보자.
일단 볼트 플러그인 설정을 하는 창으로 가서 볼트의 url을 입력해준다.
add 버튼을 누르면 신원 제공자 세팅을 할 수 있는데, 여기에서 approle을 골라서 위의 값들을 넣어준다.
pipeline {
agent any
environment {
VAULT_ADDR = 'http://172.18.0.4:30000'
}
stages {
stage('Read Vault Secret') {
steps {
withVault([
vaultSecrets: [
[
path: 'secret/sampleapp/config',
engineVersion: 2,
secretValues: [
[envVar: 'USERNAME', vaultKey: 'username'],
[envVar: 'PASSWORD', vaultKey: 'password']
]
]
],
configuration: [
vaultUrl: "${VAULT_ADDR}",
vaultCredentialId: 'vault-role'
]
]) {
sh '''
echo "Username from Vault: $USERNAME"
echo "Password from Vault: $PASSWORD"
'''
script {
echo "Username (env): ${env.USERNAME}"
echo "Password (env): ${env.PASSWORD}"
}
}
}
}
}
}
볼트 주소에는 클러스터에서 노드포트로 서비스를 열고 있기 때문에 아무 노드의 주소를 적어주면 된다.
위에서 신원 세팅을 했던 이름을 그대로 설정 블록에 넣어주면 된다.
참고로 kv 버전 1의 경우 path를 설정할 때 중간에 data를 넣어주어야 한다고 한다.
위에서 말한 거랑 반대인 격이라 헷갈리지 말자..
성공적으로 빌드가 완료됐다.
민감한 정보라는 것을 워닝으로 띄우고 있는 것이 보인다.
보다시피 볼트에서 꺼내온 데이터에 대해 콘솔 출력으로 볼 수 없도록 세팅된 것을 볼 수 있다.
아르고 cd 연동 - AVP 플러그인 활용
젠킨스에서는 approle 방식을 썼으니, 아르고에서는 다시 k8s 인증 방식을 사용해보자.
아르고 레포 서버에 특정 플러그인을 설치할 거고, 이를 위해 해당 파드가 사용하는 신원이 필요하다.
vault write auth/kubernetes/role/argo-role \
bound_service_account_names="argocd-repo-server" \
bound_service_account_namespaces="argocd" \
policies="sampleapp-policy" \
ttl="24h"
위와 같은 방식으로 세팅을 해주면, 볼트 측에서 할 설정은 끝이다.
이제 ArgoCD Vault Plugin(AVP)를 설치해야 하는데, 이게 조금 번거롭다.
물론 아르고 cd에 커스텀 플러그인을 설치하는 것이 복잡한 거고, 유달리 이 플러그인만 귀찮은 건 아니다.[4]
아무튼, 최근 권장되고 있는 사이드카 방식을 사용해본다.[5]
apiVersion: v1
kind: ConfigMap
metadata:
name: cmp-plugin
data:
avp-helm.yaml: |
---
apiVersion: argoproj.io/v1alpha1
kind: ConfigManagementPlugin
metadata:
name: argocd-vault-plugin-helm
spec:
allowConcurrency: true
# 헬름 차트 디렉토리를 찾는 필드
discover:
find:
command:
- sh
- "-c"
- "find . -name 'Chart.yaml' && find . -name 'values.yaml'"
generate:
# HELM_ARGS를 쓰면 ARGOCD_ENV_HELM_ARGS로 전달된다.
# 근데 이건 레포 서버에 임의의 코드를 실행하는 게 가능해서 정말 신뢰되는 운영자만 설정할 수 있어야 한다.
# 그러면 이게 왜 더 좋은 방식이란 거임?
# 아무튼 받은 values 파일에 들어갈 시크릿 값을 적절하게 바꾸어 생성해준다.
command:
- sh
- "-c"
- |
helm template $ARGOCD_APP_NAME -n $ARGOCD_APP_NAMESPACE ${ARGOCD_ENV_HELM_ARGS} . |
argocd-vault-plugin generate - -s argocd-vault-plugin-credentials
lockRepo: false
플러그인 사이드카 양식을 먼저 컨피그맵을 생성한다.
이 파일은 이후 사이드카 컨테이너가 고대로 사용하게 될 설정 파일이다.
실습 예제가 헬름이기 때문에 헬름 설정을 하는 것이고, 쿠스토마이즈나 일반 양식 파일이면 조금씩 다르게 설정해야 한다.[6]
여기 설정에 기입된 이름이 아르고 cd에서 실제로 플러그인 이름으로서 인식될 것이다.
가장 아래 필드에 보면 -s argocd-vault-plugin-credentials
가 있는데, 여기에 아르고 cd가 실질적으로 볼트 서버와 통신할 수 있도록 설정이 기입된 시크릿을 넣어주면 된다.
(근데 왜 내가 이걸 문서를 짜깁기해가며 추론하고 있어야 하나)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: argo:argocd-repo-server
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: argo:argocd-repo-server
subjects:
- kind: ServiceAccount
name: argocd-repo-server
namespace: argocd
roleRef:
kind: ClusterRole
name: argo:argocd-repo-server
apiGroup: rbac.authorization.k8s.io
---
kind: Secret
apiVersion: v1
metadata:
name: argocd-vault-plugin-credentials
namespace: argocd
type: Opaque
stringData:
VAULT_ADDR: "http://vault.vault.svc:8200"
AVP_TYPE: "vault"
AVP_AUTH_TYPE: "k8s"
AVP_K8S_ROLE: "argo-role"
시크릿을 만들고, 아울러 이 시크릿을 아르고 레포 서버가 불러올 수 있도록 권한 세팅을 해준다.
시크릿에 auth type을 k8s로 지정했기에 쿠버 인증 방식을 사용하는 것이고, approle 방식으로 하는 것도 가능하다.
automountServiceAccountToken: true
# 볼륨 쪽
volumes:
- configMap:
name: cmp-plugin
name: cmp-plugin
- name: custom-tools
emptyDir: {}
# 초기화 컨테이너쪽
initContainers:
- name: download-tools
image: registry.access.redhat.com/ubi8
env:
- name: AVP_VERSION
value: 1.16.1
command: [sh, -c]
args:
- >-
curl -L https://github.com/argoproj-labs/argocd-vault-plugin/releases/download/v$(AVP_VERSION)/argocd-vault-plugin_$(AVP_VERSION)_linux_amd64 -o argocd-vault-plugin &&
chmod +x argocd-vault-plugin &&
mv argocd-vault-plugin /custom-tools/
volumeMounts:
- mountPath: /custom-tools
name: custom-tools
## 컨테이너 쪽
containers:
- name: avp-helm
command: [/var/run/argocd/argocd-cmp-server]
image: quay.io/argoproj/argocd:v2.7.9
securityContext:
runAsNonRoot: true
runAsUser: 999
volumeMounts:
- mountPath: /var/run/argocd
name: var-files
- mountPath: /home/argocd/cmp-server/plugins
name: plugins
- mountPath: /tmp
name: tmp
# Register plugins into sidecar
- mountPath: /home/argocd/cmp-server/config/plugin.yaml
subPath: avp-helm.yaml
name: cmp-plugin
# Important: Mount tools into $PATH
- name: custom-tools
subPath: argocd-vault-plugin
mountPath: /usr/local/bin/argocd-vault-plugin
이제 거의 다 왔다.
아르고 repo 서버 디플로이먼트에 위의 양식을 부분부분 추가해준다.
간단하게만 요약하자면,
초기화 컨테이너를 통해 먼저 플러그인 명령 툴을 받고, 이걸 사이드카 컨테이너로 띄워서 해당 명령어를 실행할 수 있는 상태로 만들어둔다.
플러그인이 필요한 시점에 띄워져있는 사이드카로 띄워진 argocd-cmp-server는 위에서 설정한 컨피그맵의 설정 파일에 명시된 명령어를 발동시킬 것이다.
초기화 컨테이너로 받은 명령툴은 컨맵 설정 파일에서 명시돼있으므로 이때 사용되게 된다.
쿠스토마이즈를 활용하면 이 설정들을 병합하는 방식으로 쉽게 설정할 수 있는데, 나는 아직 익숙치 않아 edit으로 수정했다.
기다리다보면 알아서 뚝딱 된다.
설치가 제대로 됐다면, 이렇게 ui에서 새로운 어플리케이션을 배포하려할 때 위와 같이 플러그인 관련 상태가 표시된다.
(최하단에 Directory 칸을 누르면 플러그인도 볼 수 있게 돼있다.)
그럼 이제 아르고 cd에서 볼트를 사용할 준비가 끝난 것이다.
ui로 본 김에 그대로 아르고 세팅 자체도 ui로 세팅하기로 마음 먹었다.
사이드카 방식의 플러그인 설치에서는 플러그인을 yaml 파일로 명시하지 말라는 문서가 있길래, 일단 당장 조금 더 확실해보이는 방법을 선택했다.
kubectl apply -n argocd -f - <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: demo
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
destination:
namespace: argocd
server: https://kubernetes.default.svc
project: default
source:
path: infra/helm
repoURL: https://github.com/hyungwook0221/spring-boot-debug-app
targetRevision: main
syncPolicy:
automated:
prune: true
selfHeal: true
EOF
실습에서 제공해준 yaml 파일은 이렇게 생겼으니, 이거보고 알아서 세팅하자.
플러그인에 인자로 전달하는 new-values.yaml
은 무어냐, 실습 레포를 보면 답을 알 수 있다.[7]
꺽쇠로 닫힌 부분에 대해 위 플러그인이 작동해 볼트 서버에 요청을 날려 시크릿을 받아와 해당 부분을 채워주는 것이다!
세팅이 완료됐다.
디플로이먼트 환경변수에 이렇게 값이 들어가면 성공이다..
결론
이전에도 볼트를 간단하게 알아보고 싶었는데, 이번에 사용해보니 사용 자체는 굉장히 쉬웠다.
물론 운영을 하면서 보안 관련, 협업 관련 세팅을 하다보면 머리가 깨지는 상황이 종종 있긴 할 것 같은데, 그래도 볼트를 사용해서 보안 위협을 조금이나마 줄일 수 있다면 그정도는 충분히 감내할 수 있는 고통이 아닐까.
Vagrant도 그렇고 Terraform도 그렇고 뭔가 유용하게 쓰다보면 항상 하시코프인 경우가 참 많은 것 같다.
볼트의 각종 기능들을 쿠버네티스의 리소스로서 관리할 수 있는 VSO도 있는데, 이건 직접적으로 실습을 하지는 못했다.
여담
볼트 문서를 보면서 k8s 인증 세팅을 할 때, 헤맨 지점이 있었다.
위 문구처럼 세팅을 했다고 생각했는데, 단순하게 이렇게 하는 것만으로는 해결이 되지 않았다.
AVP 문서는.. 좀 반성 좀 해야할 것 같다..
문서를 무슨 퍼즐 풀듯이 이거 봤다 저거봤다 하면서 짜맞추기해서 설정했다...
이전 글, 다음 글
다른 글 보기
이름 | index | noteType | created |
---|---|---|---|
1W - EKS 설치 및 액세스 엔드포인트 변경 실습 | 1 | published | 2025-02-03 |
2W - 테라폼으로 환경 구성 및 VPC 연결 | 2 | published | 2025-02-11 |
2W - EKS VPC CNI 분석 | 3 | published | 2025-02-11 |
2W - ALB Controller, External DNS | 4 | published | 2025-02-15 |
3W - kubestr과 EBS CSI 드라이버 | 5 | published | 2025-02-21 |
3W - EFS 드라이버, 인스턴스 스토어 활용 | 6 | published | 2025-02-22 |
4W - 번외 AL2023 노드 초기화 커스텀 | 7 | published | 2025-02-25 |
4W - EKS 모니터링과 관측 가능성 | 8 | published | 2025-02-28 |
4W - 프로메테우스 스택을 통한 EKS 모니터링 | 9 | published | 2025-02-28 |
5W - HPA, KEDA를 활용한 파드 오토스케일링 | 10 | published | 2025-03-07 |
5W - Karpenter를 활용한 클러스터 오토스케일링 | 11 | published | 2025-03-07 |
6W - PKI 구조, CSR 리소스를 통한 api 서버 조회 | 12 | published | 2025-03-15 |
6W - api 구조와 보안 1 - 인증 | 13 | published | 2025-03-15 |
6W - api 보안 2 - 인가, 어드미션 제어 | 14 | published | 2025-03-16 |
6W - EKS 파드에서 AWS 리소스 접근 제어 | 15 | published | 2025-03-16 |
6W - EKS api 서버 접근 보안 | 16 | published | 2025-03-16 |
7W - 쿠버네티스의 스케줄링, 커스텀 스케줄러 설정 | 17 | published | 2025-03-22 |
7W - EKS Fargate | 18 | published | 2025-03-22 |
7W - EKS Automode | 19 | published | 2025-03-22 |
8W - 아르고 워크플로우 | 20 | published | 2025-03-30 |
8W - 아르고 롤아웃 | 21 | published | 2025-03-30 |
8W - 아르고 CD | 22 | published | 2025-03-30 |
8W - CICD | 23 | published | 2025-03-30 |
9W - EKS 업그레이드 | 24 | published | 2025-04-02 |
10W - Vault를 활용한 CICD 보안 | 25 | published | 2025-04-16 |
11주차 - EKS에서 FSx, Inferentia 활용하기 | 26 | published | 2025-05-11 |
11W - EKS에서 FSx, Inferentia 활용하기 | 26 | published | 2025-04-18 |
12W - VPC Lattice 기반 gateway api | 27 | published | 2025-04-27 |
관련 문서
이름 | noteType | created |
---|---|---|
Vault | knowledge | 2025-04-11 |
볼트 in 쿠버네티스 | knowledge | 2025-04-14 |
Vault Secret Operator | knowledge | 2025-04-14 |
E-이스티오 메시 스케일링 | topic/explain | 2025-06-08 |
참고
https://developer.hashicorp.com/vault/docs/concepts/policies#policy-syntax ↩︎
https://developer.hashicorp.com/vault/docs/commands#configure-environment-variables ↩︎
https://support.hashicorp.com/hc/en-us/articles/4404389946387-Kubernetes-auth-method-Permission-Denied-error ↩︎
https://argo-cd.readthedocs.io/en/stable/operator-manual/config-management-plugins/#installing-a-config-management-plugin ↩︎
https://argocd-vault-plugin.readthedocs.io/en/stable/installation/#initcontainer-and-configuration-via-sidecar ↩︎
https://github.com/argoproj-labs/argocd-vault-plugin/blob/main/manifests/cmp-sidecar/cmp-plugin.yaml ↩︎
https://github.com/hyungwook0221/spring-boot-debug-app/blob/main/infra/helm/new-values.yaml ↩︎